package com.xxkk.spring_boot_web.controller;

import com.alibaba.fastjson.JSONObject;
import com.xxkk.spring_boot_web.module.OrderState;
import com.xxkk.spring_boot_web.sdk.xnetPay.SignUtil;
import com.xxkk.spring_boot_web.sdk.xnetPay.xnetPaySDK;
import com.xxkk.spring_boot_web.service.RechargeGameCoin;
import com.xxkk.spring_boot_web.service.SqlService;
import com.xxkk.spring_boot_web.sql.ConfigDao;
import com.xxkk.spring_boot_web.sql.pojo.RechargeOrder;
import com.xxkk.spring_boot_web.utils.MysqlSyncUtil;
import com.xxkk.spring_boot_web.utils.SnowflakeIdFactory;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;

/**
 * PAI文档：https://www.showdoc.com.cn/XnetPay
 * 文档密码：pay123
 * PAI网关：https://xnetpay.net
 * 商户编号：12345678
 * 商户密钥：6f559400399c28dc94497aa89af5cdf2
 * 测试支付类型：Trade_type值
 * 代收:QUICK.QRIS
 * 代付: DEFRAY.BANKVA
 */
@Controller
@RequestMapping(value = "/PayWeb/pay")
public class payCtl {
    public static final String payNamePrefix = "xnetPay";

    @Autowired
    private SqlService sqlService;
    @Autowired
    private ConfigDao config;
    private final SnowflakeIdFactory idFactory = new SnowflakeIdFactory(3, 2);
    private static final Logger logger = Logger.getLogger(payCtl.class);

    private static String SUCCESS(String state, String msg, String data) {
        JSONObject respVo = new JSONObject();
        respVo.put("state", state);
        respVo.put("msg", msg);
        respVo.put("data", data);
        return respVo.toJSONString();
    }

    private static String FAIL(String state, String msg, String data) {
        return SUCCESS(state, msg, data);
    }

    private long generatorId() {
        return idFactory.nextId();//生成订单编号 雪花算法 高并发 唯一性
    }

    @RequestMapping("/recharge") //下单请求
    @ResponseBody
    public String order(@RequestBody Map<String, Object> cipherMap, HttpServletRequest request, HttpServletResponse response) {
        try {
            JSONObject paramJson = new JSONObject(cipherMap);

            String channel = paramJson.getString("channel");
            Objects.requireNonNull(channel);
            String channel_id = paramJson.getString("channel_id");
            Objects.requireNonNull(channel_id);
            String roleId = paramJson.getString("roleId");
            if (this.sqlService.getRechargeTodayTimes(roleId) > 100)
                return FAIL("OUT_OF_LIMITS", "充值失败: 玩家充值超过每日次数限制", null);
            String orderId = paramJson.getString("orderId");
            if (orderId == null)
                orderId = payNamePrefix + this.generatorId() + channel_id + roleId; //生成订单 订单号：具有可读性 14位时间戳+主键自增+渠道号（字符串）+玩家id

            String payMoney = paramJson.getString("payMoney");
            int pay_goods_id = paramJson.getIntValue("pay_goods_id");
            String gc = paramJson.getString("gameCoin");
            int gameCoin = gc != null ? Integer.parseInt(gc) : (int) (Integer.parseInt(payMoney) * config.getFloat("recharge_money_gameCoin_rate"));
            paramJson.put("gameCoin", gameCoin);
            logger.info(payNamePrefix + "代收下单请求：\n" + MysqlSyncUtil.formatJson(paramJson.toJSONString()));
            String name = paramJson.getString("name");
            String phone = paramJson.getString("phone");
            String email = paramJson.getString("email");

            // sal保存订单记录
            RechargeOrder table = new RechargeOrder();
            table.setRoleId(roleId);
            table.setMchOrderNum(orderId);
            table.setCreateTime(new Date());
            table.setUpdateTime(new Date());
            table.setChannelType(channel);
            table.setChannel_id(channel_id);
            table.setPayMoney(new BigDecimal(payMoney));
            table.setGameCoin((long) gameCoin);
            table.setState(OrderState.submit.name());
            table.setPay_goods_id(pay_goods_id);
            this.sqlService.addOrder(table);

            TreeMap<String, String> map = new TreeMap<String, String>();
            map.put("mch_id", config.getString("xnetPay_mch_id"));// 印尼商户号
            map.put("subject", pay_goods_id + "");// 商品名称
            map.put("body", pay_goods_id + "");// 商品描述
            String trade_type = paramJson.getString("trade_type");
            map.put("trade_type", trade_type == null ? "QUICK.QRIS" : trade_type);//QUICK.GCASH // 银行虚拟账户支付 QUICK.BANKVA // QUICK.QRIS
            map.put("total_fee", payMoney); // 支付金额（印尼盾）
            map.put("out_trade_no", orderId);
            map.put("notify_url", config.getString("xnetPay_recharge_notifyUrl") + "/PayWeb/pay/order_cb");// 回调地址
            map.put("return_url", config.getString("xnetPay_return_url"));
            map.put("currency", "IDR"); // 币种：印尼盾
            JSONObject attach = new JSONObject();
            attach.put("phone", phone);
            attach.put("email", email);
            attach.put("name", name);
            String ectCode = paramJson.getString("ectCode");
            attach.put("ectCode", ectCode == null ? "QRIS" : ectCode);

            map.put("attach", attach.toJSONString());// 额外参数
            map.put("sign", SignUtil.buildSign(map, config.getString("xnetPay_mch_key")));// 印尼商户密钥

            String body = JSONObject.toJSONString(map);
            String url = config.getString("xnetPay_BaseUrl");
            if (url == null)
                url = "https://xnetpay.net";
            url = url + "/itnpay/unifiedorder";//代收订单请求地址
            logger.info(payNamePrefix + " " + url + " 发起下单请求\n：" + MysqlSyncUtil.formatJson(body));
            String res = xnetPaySDK.request(url, body);//请求
            logger.info(payNamePrefix + " 响应下单请求\n:" + MysqlSyncUtil.formatJson(res));
            JSONObject json = JSONObject.parseObject(res);
            table.setJsonParam(json.toJSONString());//含支付订单请求状态说明

            if (!"SUCCESS".equals(json.getString("return_code"))) {
                updateOrderState(OrderState.mch_recharge_request_fail, table);
                return FAIL("FAIL", payNamePrefix + "平台支付交易失败", null);
            }

            String platOrderNum = json.getString("order_no");//平台生成的订单号
            table.setPlatOrderNum(platOrderNum);
            this.sqlService.updateOrder(table);
            return SUCCESS("SUCCESS", null, json.getString("pay_info"));
        } catch (Exception e) {
            logger.error(e, e);
        }
        return FAIL("FAIL", "下单失败", null);
    }

    private String resp_param(int code, String msg, String data) {
        JSONObject json = new JSONObject();
        json.put("code", code);
        json.put("msg", msg);
        json.put("data", data);
        return json.toJSONString();
    }

    private String success_() {
        return resp_param(200, "SUCCESS", "{}");
    }

    private String fail_(String msg) {
        return resp_param(200, msg, null);
    }

    private final String SUCCESS = "SUCCESS";

    @RequestMapping("/order_cb")
    @ResponseBody    //下单回调接口
    public String orderCB(@RequestParam Map<String, String> params, HttpServletRequest request, HttpServletResponse response) {
        try {
            JSONObject json = (JSONObject) JSONObject.toJSON(params);
            String json_str = json.toJSONString();
            logger.info(payNamePrefix + "代收支付回调:\n" + MysqlSyncUtil.formatJson(json_str));
            boolean verifyResult = xnetPaySDK.verifySign(params, config.getString("xnetPay_mch_key"));
            if (!verifyResult) {// 签名验证失败
                logger.info("签名验证失败");
                return SUCCESS;
            }
            String orderNum = json.getString("out_trade_no");//商户订单号
            BigDecimal payMoney = json.getBigDecimal("total_fee");
            String order_no = json.getString("order_no");//平台系统订单号
            String order_status = json.getString("order_status");//订单状态，等待支付-wait,支付成功-success
            RechargeOrder order = this.sqlService.getOrder(orderNum);
            if (order == null) {
                logger.info("充值失败：通知回调订单不存在");
                return SUCCESS;
            }
            if (payMoney.compareTo(order.getPayMoney()) != 0) {
                logger.info("充值失败：订单验证金额不一致 数据库订单记录要求金额：" + order.getPayMoney());
                return SUCCESS;
            }
            if (!OrderState.submit.name().equals(order.getState())) {
                logger.info("充值失败：订单状态异常，当前订单状态为 " + order.getState());
                return SUCCESS;
            }
            if (!order.getPlatOrderNum().equals(order_no)) {
                logger.info("平台订单号不一致 充值失败 请求订单号：" + order_no + " 下单订单号：" + order.getPlatOrderNum());
                return SUCCESS;
            }
            if (order_status == null || !order_status.equalsIgnoreCase(SUCCESS)) {
                this.updateOrderState(OrderState.recharge_fail, order);
                return SUCCESS;
            }
            updateOrderState(OrderState.recharged, order);
            //发放奖励
            String recharge_game_coin_url = config.getConfig("java_game_server_address") + "/g20-web/gm/recharge_game_coin";
            String roleId = order.getRoleId();
            long coin = order.getGameCoin();
            Map<String, Object> order_map = new TreeMap<>();
            order_map.put("roleId", roleId);
            order_map.put("coin", coin);
            order_map.put("orderId", order.getMchOrderNum());
            order_map.put("pay_goods_id", order.getPay_goods_id());

            order_map.put("platOrderNum", order.getPlatOrderNum());
            order_map.put("channelType", order.getChannelType());
            order_map.put("channel_id", order.getChannel_id());
            order_map.put("recharge_plat_type", payNamePrefix);

            order_map.put("is_first_recharge", this.sqlService.isFirstRecharge(roleId));//判断是否为首充

            String recharge_g_c_res = RechargeGameCoin.recharge(recharge_game_coin_url, order_map, config.getConfig("pay_and_java_server_rsa_private_key"));
            logger.info(recharge_g_c_res);
            if (recharge_g_c_res == null) {
                logger.info("请求游戏服务器失败:" + recharge_game_coin_url);
                updateOrderState(OrderState.add_coin_fail, order);
//                return fail_("请求游戏服务器失败:" + recharge_game_coin_url);
                return SUCCESS;
            }
            JSONObject jsonObject = JSONObject.parseObject(recharge_g_c_res);
            order.setState(jsonObject.getString("state"));//刷新订单状态 SUCCESS
            this.sqlService.updateOrder(order);
            return SUCCESS;
        } catch (Exception e) {
            logger.error(e, e);
        }
        return fail_("服务异常");
    }

    private void updateOrderState(OrderState state, RechargeOrder order) {
        order.setState(state.code < 100 ? state.name() : state.desc_);
        order.setUpdateTime(new Date());
        this.sqlService.updateOrder(order);
    }

}
